package container;

import java.util.*;

/**
 * https://blog.csdn.net/f641385712/article/details/80423118
 */
public class Constants {
    public static final int COUNT = 1000000;
    public static final int THREAD_COUNT = 100;

    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        System.out.println(list); // [aa, bb, cc]
        list.remove("aa"); // 字符串可以
        System.out.println(list); // [bb, cc]

        list.clear();
        list.add(2);
        list.add(1);
        list.add(0);
        System.out.println(list); // [2, 1, 0]
        list.remove(2); // 直接删除不行
        // list.remove((Object) 2);
        System.out.println(list); // [2, 1]

        mapRemove();
        // listRemove();
    }

    public static Map<Integer, Integer> mapPut() {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(1, 1);
        map.put(2, 2);
        map.put(5, 5);
        map.put(3, 3);
        map.put(10, 10);
        System.out.println(map.put(8, 2)); // null，如果是添加成功，返回 null
        System.out.println(map.put(8, 3)); // 2，如果已经存在此 key 了，会用新值覆盖旧值，然后把 oldValue 返回出来
        System.out.println(map.putIfAbsent(9, 2)); // null，如果是添加成功，返回 null
        System.out.println(map.putIfAbsent(9, 3)); // 2，如果已经存在此 key 了，不会覆盖此 value 值。返回的还是 oldValue

        // 若 key 不存在（或 key 对应的 value 为 null），就去计算 mappingFunction 的值（计算结果决定了返回值有两种情况如下，姑且叫计算的结果为 newValue）：
        // newValue == null，不会替换旧值。返回 null
        // newValue != null，替换旧值。返回新值
        // 若 key 已存在，就不会执行 mappingFunction。返回 oldValue
        System.out.println(map.computeIfAbsent(11, key -> Integer.valueOf(key) + 2)); // 13，计算出来的 newValue 放进去，并且返回 newValue
        System.out.println(map.computeIfAbsent(11, key -> Integer.valueOf(key) + 3)); // 13，key 已经存在了，不作为
        System.out.println(map.computeIfAbsent(12, key -> null)); // null，key 虽然不存在，但计算出为 null，所以不作为，并且返回 null

        // 和 computeIfAbsent 相反，当 key 存在且对应的 oldValue 不为 null 时，执行计算，否则啥都不做
        System.out.println(map.computeIfPresent(6, (k, v) -> Integer.valueOf(k) + v)); // null，key 不存在，不作为，且返回 null
        System.out.println(map.computeIfPresent(6, (k, v) -> Integer.valueOf(k) + v)); // null，key 还是不存在，不作为，且返回 null
        map.put(6, 1);
        System.out.println(map.computeIfPresent(6, (k, v) -> Integer.valueOf(k) + v)); // 7，key 存在，开始计算。把计算的结果覆盖掉，然后返回计算的结果
        System.out.println(map.computeIfPresent(6, (k, v) -> null)); // null，key 存在，开始计算，但是计算结果为 null。所以返回 null，此处注意 remove() 掉了对应的 key

        return map;
    }

    public static void mapRemove() {
        Map<Integer, Integer> map = mapPut();

        // map.keySet().removeIf(key -> key >= 5);
        Iterator<Integer> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            Integer key = iterator.next();
            if (key >= 5) {
                // map.remove(key); // java.util.ConcurrentModificationException
                iterator.remove(); // 调用 iterator.remove()，不要用 map.remove(key);
            }
        }
        System.out.println(map);
    }

    public static void listRemove() {
        // UnsupportedOperationException
        // 这样生成的 list 其实是 Arrays 自己实现的一个阉割版的 List，它是没有实现 remove 方法的，所以无法实现删除操作
        List<Integer> arraysList = Arrays.asList(2, 1, 3, 5, 8, 6, 2, 5, 10);
        List<Integer> list = new ArrayList<>();
        list.add(2);
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(8);
        list.add(6);
        list.add(2);
        list.add(5);
        list.add(10);
        int baseNum = 9; // 以这个为基础 删除掉 >= 这个值的元素

        // 1、普通 for 循环遍历
        // 这种删除方式肯定是有问题的：删除某个元素后，list 的大小发生了变化，而索引也在变化，所以会导致在遍历的时候漏掉某些元素
        System.out.println("剩余长度：" + list.size() + "---" + list);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) >= baseNum)
                list.remove(i);
        }
        System.out.println("剩余长度：" + list.size() + "---" + list);

        // 2、增强 for 循环，底层是迭代器实现的，删除调用的是 list 的 remove 方法
        // 删除元素后继续循环会报错误信息 ConcurrentModificationException，因为元素在使用的时候发生了并发的修改，导致异常抛出(fast-failed)
        // 但是如果我们只需要删除一个元素，马上使用 break 跳出，则不会触发报错
        System.out.println("剩余长度：" + list.size() + "---" + list);
        for (Integer x : list) {
            if (x >= baseNum) {
                /** @see ArrayList.Itr#next() 触发 fast-failed */
                list.remove(x); // java.util.ConcurrentModificationException
            }
        }
        System.out.println("剩余长度：" + list.size() + "---" + list);

        // 3、迭代器 iterator 遍历
        System.out.println("剩余长度：" + list.size() + "---" + list);
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            Integer x = it.next();
            if (x >= baseNum)
                it.remove();
        }
        System.out.println("剩余长度：" + list.size() + "---" + list);

        // 方法一：使用 CopyOnWriteArrayList 支持并发修改的 List 来代替，缺点就是效率低
        // 方法二：采用对 List 倒序遍历的方式。缺点是：理解难度相较于迭代器偏大，需要结合 List 内部实现才能真正理解。当然因为 size 需要实时计算，速度上也不占优势
        System.out.println("剩余长度：" + list.size() + "---" + list);
        // for (int i = 0; i < list.size(); i++) {
        for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i) >= baseNum)
                list.remove(i);
        }
        System.out.println("剩余长度：" + list.size() + "---" + list);
    }
}